import java.awt.*;
import java.io.*;
import java.awt.geom.*;

public class DirectedEdge
extends Selectable
implements Serializable
{
    
    public int multi=1;
  // The current calculated <cos,sin> and length of this edge.
    public transient Position rotation = new Position(0,0,0);
    public transient double length = -1;

  DirectedVertex start;
  DirectedVertex end;

  // Serializable
  private void writeObject(ObjectOutputStream out)
  throws IOException {
    out.writeObject(start);
    out.writeObject(end);
    out.writeInt(multi);
  }

  // Serializable
  private void readObject(ObjectInputStream in)
  throws IOException, ClassNotFoundException {
    start = (DirectedVertex)in.readObject();
    end = (DirectedVertex)in.readObject();
    multi=in.readInt();
    length = -1;
    rotation = new Position(0,0,0);
  }

  // Calculate length and rotation.
  public final void validate() {
    if (length<0) {
      Position p1 = start.position;
      Position p2 = end.position;     
      length = Position.distance(p1,p2);
      if (length==0) return;
      double dy = p2.y - p1.y;
      double dx = p2.x - p1.x;
      rotation.x = dx/length;
      rotation.y = dy/length; 
    }
  }
  
  // Force recalculation of length and rotation.
  public final void revalidate() {
    length = -1;
    validate();
  }

  public DirectedEdge(DirectedVertex a, DirectedVertex b) {
      super();
    start = a;
    end = b;    
     revalidate();
  }
  
  public String toString() {
    String s = "(Edge";
    s += " " + String.valueOf(start.hashCode());
    s += " " + String.valueOf(end.hashCode());
    s += " " + multi;
    s += " " + super.toString() + " ";
    return s+" )";
  }
  
  // What is the length of the shortest segment perpendicular to this edge
  // that touches the edge and the point x,y?
  // Return a number >10000 if there is no such segment.
  public final double distance(double x,double y) {
    validate();
    Position p = start.position;
    x -= p.x;
    y -= p.y;
    double cos = rotation.x;
    double sin = rotation.y;
    double tx = x*cos+y*sin;
    if ((length==0) || (tx<0) || (tx>length)) return length+10000;
    double ty = -x*sin+y*cos;
    ty = Math.abs(ty);
    return ty;
  }
  
  public final boolean nearStart(int x,int y) {
    int limit = 10+size+start.size;
    return start.near(x,y,limit);
  }

  public final boolean nearEnd(int x,int y) {
    int limit = 10+size+end.size;
    return end.near(x,y,limit);
  }

  public final void paintLine(Graphics g) {
    validate();
    Graphics2D g2d = (Graphics2D) g;
    if (!this.isSelected()){
    	if (start.color==Color.green){
    		g.setColor(Color.green);}
    	else if (start.color == Color.red){
    		g.setColor(Color.red);
    	}
    	else 
    		g.setColor(getColor(color));
    }
    else if (this.isSelected())
    	g.setColor(getColor(color));
    
    Position p1 = start.position;
    Position p2 = end.position;
    // To enable thickness of edge,   
    BasicStroke s = new BasicStroke(2);
    g2d.setStroke(s);   
    g2d.drawLine(p1.x(),p1.y(),p2.x(),p2.y());
  }

  public final void adjustSize(int d) {
    int r = size + d;
    if (r>2) size = r;
  }
  
  public final void paintEndArrow(Graphics g) {
    Position p1 = start.position;
    Position p2 = end.position;
    Graphics2D g2d= (Graphics2D)g;
    GeneralPath arrow= new GeneralPath();
    if (start.getColor(color)==Color.red){
    	arrow.moveTo(p2.x-10,p2.y-10);
        arrow.lineTo(p2.x+10,p2.y+10);
        arrow.moveTo(p2.x+10,p2.y-10);
        arrow.lineTo(p2.x-10,p2.y+10);
        arrow.closePath();
    }
    else {
    	arrow.moveTo(p2.x,p2.y);
        arrow.lineTo(p2.x-10,p2.y-10);
        arrow.lineTo(p2.x+10,p2.y-10);
        arrow.lineTo(p2.x,p2.y);
        arrow.closePath();
    }
    g2d.draw(arrow);  
  }

  public final void paintArrows(Graphics g) {
   revalidate();
    if (length==0) return;
      paintEndArrow(g);
  
  }

  public final void defaultLabel() {
    revalidate();
    int x = ((int)(start.position.x+end.position.x)) >> 1;
    int y = ((int)(start.position.y+end.position.y)) >> 1;
    moveLabel(x,y);
  }

  // Shift the position.
  public final void moveRelative(double x,double y) {
    moveLabelRelative((int)x,(int)y);
  }

  public final void paint(Graphics g) {
    paintLine(g);
    paintArrows(g);
  }
}






